﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ERF;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace FirstPerson.Objects {
    /// <summary>
    /// Represents a skybox consisting of 6 planes. Supports blending between two sets of sky textures.
    /// </summary>
    public class Skybox : GameObject
    {
        private GameTime currentGameTime;
        private Matrix parentMatrix = Matrix.Identity;
        private Plane topPlane, leftPlane, rightPlane, frontPlane, backPlane;
        private bool useBlending = false;
        private float blend;
        public Skybox(ERFGame game, string sky)
            : base(game) {
            SkyboxShader effect;

            SkyTextures skyTextures = LoadSkybox(sky);

            this.topPlane = new Plane(game);
            this.topPlane.Rotation = new Vector3(MathHelper.Pi, 0, 0);
            this.topPlane.Position = new Vector3(0, 0.5f, 0);
            this.topPlane.RenderWithoutDepthBuffer = true;
            effect = new SkyboxShader(game);
            effect.TextureA = skyTextures.topTexture;
            effect.UseBlend = false;
            this.topPlane.Effect = effect;

            this.leftPlane = new Plane(game);
            this.leftPlane.Rotation = new Vector3(0, MathHelper.PiOver2, -MathHelper.PiOver2);
            this.leftPlane.Position = new Vector3(-0.5f, 0, 0);
            this.leftPlane.RenderWithoutDepthBuffer = true;
            effect = new SkyboxShader(game);
            effect.TextureA = skyTextures.leftTexture;
            effect.UseBlend = false;
            this.leftPlane.Effect = effect;

            this.rightPlane = new Plane(game);
            this.rightPlane.Rotation = new Vector3(0, -MathHelper.PiOver2, MathHelper.PiOver2);
            this.rightPlane.Position = new Vector3(0.5f, 0, 0);
            this.rightPlane.RenderWithoutDepthBuffer = true;
            effect = new SkyboxShader(game);
            effect.TextureA = skyTextures.rightTexture;
            effect.UseBlend = false;
            this.rightPlane.Effect = effect;

            this.frontPlane = new Plane(game);
            this.frontPlane.Rotation = new Vector3(MathHelper.PiOver2, 0, 0);
            this.frontPlane.Position = new Vector3(0, 0, -0.5f);
            this.frontPlane.RenderWithoutDepthBuffer = true;
            effect = new SkyboxShader(game);
            effect.TextureA = skyTextures.frontTexture;
            effect.UseBlend = false;
            this.frontPlane.Effect = effect;

            this.backPlane = new Plane(game);
            this.backPlane.Rotation = new Vector3(-MathHelper.PiOver2, 0, MathHelper.Pi);
            this.backPlane.Position = new Vector3(0, 0, 0.5f);
            this.backPlane.RenderWithoutDepthBuffer = true;
            effect = new SkyboxShader(game);
            effect.TextureA = skyTextures.backTexture;
            effect.UseBlend = false;
            this.backPlane.Effect = effect;

            this.Children.Add(this.topPlane);
            //this.Children.Add("Bottom", this.bottomPlane);
            this.Children.Add(this.leftPlane);
            this.Children.Add(this.rightPlane);
            this.Children.Add(this.frontPlane);
            this.Children.Add(this.backPlane);

            this.scale = new Vector3(20, 20, 20);
        }

        public override Vector3 Position
        {
            get
            {
                return base.Position;
            }
            set
            {
                base.Position = value;
                base.UpdateWorld(ref this.parentMatrix);
                base.Update(this.currentGameTime, ref base.world);
            }
        }


        /// <summary>
        /// Loads a the given skybox.
        /// </summary>
        /// <param name="skybox">The skybox asset name.</param>
        /// <returns>A SkyTextures instance containing the texture for all planes.</returns>
        private SkyTextures LoadSkybox(string skybox)
        {
            SkyTextures sky = new SkyTextures(base.CurrentGame, skybox);
            return sky;
        }

        /// <summary>
        /// Sets the textures used for one of the two skies.
        /// </summary>
        /// <param name="sky">The skybox asset name.</param>
        public void SetTextureA(string sky)
        {
            SkyTextures skyTextures = this.LoadSkybox(sky);
            (this.topPlane.Effect as SkyboxShader).TextureA = skyTextures.topTexture;
            (this.backPlane.Effect as SkyboxShader).TextureA = skyTextures.backTexture;
            (this.frontPlane.Effect as SkyboxShader).TextureA = skyTextures.frontTexture;
            (this.leftPlane.Effect as SkyboxShader).TextureA = skyTextures.leftTexture;
            (this.rightPlane.Effect as SkyboxShader).TextureA = skyTextures.rightTexture;
        }

        /// <summary>
        /// Sets the textures used for one of the two skies.
        /// </summary>
        /// <param name="sky">The skybox asset name.</param>
        public void SetTextureB(string sky)
        {
            SkyTextures skyTextures = this.LoadSkybox(sky);
            (this.topPlane.Effect as SkyboxShader).TextureB = skyTextures.topTexture;
            (this.backPlane.Effect as SkyboxShader).TextureB = skyTextures.backTexture;
            (this.frontPlane.Effect as SkyboxShader).TextureB = skyTextures.frontTexture;
            (this.leftPlane.Effect as SkyboxShader).TextureB = skyTextures.leftTexture;
            (this.rightPlane.Effect as SkyboxShader).TextureB = skyTextures.rightTexture;
        }

        /// <summary>
        /// Gets or sets the value that determines how to blend the two textures used, if UseBlend is set to true.
        /// </summary>
        public float Blend
        {
            get { return this.blend; }
            set
            {
                this.blend = value;
                (this.topPlane.Effect as SkyboxShader).Blend = value;
                (this.backPlane.Effect as SkyboxShader).Blend = value;
                (this.frontPlane.Effect as SkyboxShader).Blend = value;
                (this.leftPlane.Effect as SkyboxShader).Blend = value;
                (this.rightPlane.Effect as SkyboxShader).Blend = value;
            }
        }

        /// <summary>
        /// Gets or sets a value determining wether or not to blend between the two textures used.
        /// </summary>
        public bool UseBlending
        {
            get { return this.useBlending; }
            set { 
                this.useBlending = value;
                (this.topPlane.Effect as SkyboxShader).UseBlend = value;
                (this.backPlane.Effect as SkyboxShader).UseBlend = value;
                (this.frontPlane.Effect as SkyboxShader).UseBlend = value;
                (this.leftPlane.Effect as SkyboxShader).UseBlend = value;
                (this.rightPlane.Effect as SkyboxShader).UseBlend = value;
            }
        }

        

        public override void Update(Microsoft.Xna.Framework.GameTime gameTime, ref Microsoft.Xna.Framework.Matrix world) {
            //Camera camera = base.CurrentGame.Camera;
            //this.Position = camera.Position;
            this.parentMatrix = world;
            this.currentGameTime = gameTime;
            //base.UpdateWorld(ref world);

            //base.Update(gameTime, ref base.world);
        }

        /// <summary>
        /// Represents a set of textures used for the different planes on the skybox.
        /// </summary>
        private class SkyTextures
        {
            public Texture2D topTexture, leftTexture, rightTexture, frontTexture, backTexture;
            private const int HORIZONTAL_CELLS = 6;

            public SkyTextures(ERFGame game, string sky)
            {
                this.topTexture = game.Content.Load<Texture2D>(string.Format("Textures\\Skybox\\{0}\\top", sky));
                this.backTexture = game.Content.Load<Texture2D>(string.Format("Textures\\SkyBox\\{0}\\back", sky));
                this.frontTexture = game.Content.Load<Texture2D>(string.Format("Textures\\SkyBox\\{0}\\front", sky));
                this.leftTexture = game.Content.Load<Texture2D>(string.Format("Textures\\SkyBox\\{0}\\left", sky));
                this.rightTexture = game.Content.Load<Texture2D>(string.Format("Textures\\SkyBox\\{0}\\right", sky));
            }

            //    public SkyTextures(ERFGame game, string sky)
            //    {
            //        Texture2D skyTexture = game.Content.Load<Texture2D>(
            //            string.Format("Textures\\Skybox\\{0}", sky));

            //        int cellWidth = skyTexture.Width / HORIZONTAL_CELLS;
            //        int[] data;

            //        int cellX = 0;

            //        // Right texture.
            //        data = new int[(cellWidth / 2) * (cellWidth-2)];
            //        skyTexture.GetData<int>(0, new Rectangle(cellX, 0, cellWidth - 2, cellWidth/2), data, 0, data.Length);
            //        this.rightTexture = new Texture2D(game.GraphicsDevice, cellWidth-2, cellWidth / 2);
            //        this.rightTexture.SetData<int>(data);

            //        cellX += cellWidth + 1;
            //        // Left texture.
            //        data = new int[(cellWidth / 2) * cellWidth];
            //        skyTexture.GetData<int>(0, new Rectangle(cellX, 0, cellWidth, cellWidth / 2), data, 0, data.Length);
            //        this.leftTexture = new Texture2D(game.GraphicsDevice, cellWidth, cellWidth / 2);
            //        this.leftTexture.SetData<int>(data);

            //        cellX += cellWidth + 1;
            //        // Top texture.
            //        data = new int[cellWidth * cellWidth];
            //        skyTexture.GetData<int>(0, new Rectangle(cellX, 0, cellWidth, cellWidth), data, 0, data.Length);
            //        this.topTexture = new Texture2D(game.GraphicsDevice, cellWidth, cellWidth);
            //        this.topTexture.SetData<int>(data);

            //        cellX += (2*cellWidth) + 1;
            //        // Front texture.
            //        data = new int[(cellWidth / 2) * cellWidth];
            //        skyTexture.GetData<int>(0, new Rectangle(cellX, 0, cellWidth, cellWidth / 2), data, 0, data.Length);
            //        this.frontTexture = new Texture2D(game.GraphicsDevice, cellWidth, cellWidth/2);
            //        this.frontTexture.SetData<int>(data);

            //        cellX += cellWidth + 1;
            //        // Back texture.
            //        skyTexture.GetData<int>(0, new Rectangle(cellX, 0, cellWidth, cellWidth / 2), data, 0, data.Length);
            //        this.backTexture = new Texture2D(game.GraphicsDevice, cellWidth, cellWidth / 2);
            //        this.backTexture.SetData<int>(data);
            //    }
            //}

            //#region IDisposable Members
        }
            public override void Dispose()
            {
                this.topPlane.Dispose();
                this.topPlane = null;
                this.leftPlane.Dispose();
                this.leftPlane = null;
                this.rightPlane.Dispose();
                this.rightPlane = null;
                this.frontPlane.Dispose();
                this.frontPlane = null;
                this.backPlane.Dispose();
                this.backPlane = null;
            }

            //#endregion
    }
}
